/*
  FirmataParser.h
  Copyright (c) 2006-2008 Hans-Christoph Steiner.  All rights reserved.
  Copyright (C) 2009-2016 Jeff Hoefs.  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  See file LICENSE.txt for further informations on licensing terms.
*/

#ifndef FirmataParser_h
#define FirmataParser_h

#include <cstddef>
#include <cstdint>
#include <Stream.h>

namespace firmata {

    class FirmataParser {
    public:
        /* callback function types */
        typedef void (*callbackFunction)(void *context, nStream *, uint8_t command, uint16_t value);

        typedef void (*dataBufferOverflowCallbackFunction)(void *context, nStream *);

        typedef void (*stringCallbackFunction)(void *context, nStream *, const char *c_str);

        typedef void (*sysexCallbackFunction)(void *context, nStream *, uint8_t command, size_t argc, uint8_t *argv);

        typedef void (*systemCallbackFunction)(void *context, nStream *);

        typedef void (*versionCallbackFunction)(void *context, nStream *, size_t sv_major, size_t sv_minor,
                                                const char *firmware);

        FirmataParser(uint8_t *dataBuffer = (uint8_t *) nullptr, size_t dataBufferSize = 0);

        /* serial receive handling */
        void parse(nStream *, uint8_t value);

        bool isParsingMessage(void) const;

        int setDataBufferOfSize(uint8_t *Buffer, size_t BufferSize);

        /* attach & detach callback functions to messages */
        void attach(uint8_t command, callbackFunction newFunction, void *context = nullptr);

        void attach(dataBufferOverflowCallbackFunction newFunction, void *context = nullptr);

        void attach(uint8_t command, stringCallbackFunction newFunction, void *context = nullptr);

        void attach(uint8_t command, sysexCallbackFunction newFunction, void *context = nullptr);

        void attach(uint8_t command, systemCallbackFunction newFunction, void *context = nullptr);

        void attach(uint8_t command, versionCallbackFunction newFunction, void *context = nullptr);

        void detach(uint8_t command);

        void detach(dataBufferOverflowCallbackFunction);

        size_t dataBufferSize;
        /* input message handling */
        bool allowBufferUpdate;
        uint8_t *dataBuffer; // multi-byte data
        uint8_t executeMultiByteCommand; // execute this after getting multi-byte data
        uint8_t multiByteChannel;        // channel data for multiByteCommands
        size_t waitForData;              // this flag says the next serial input will be data

        /* sysex */
        bool parsingSysex;
        size_t sysexBytesRead;

        /* callback context */
        void *currentAnalogCallbackContext;
        void *currentDigitalCallbackContext;
        void *currentReportAnalogCallbackContext;
        void *currentReportDigitalCallbackContext;
        void *currentPinModeCallbackContext;
        void *currentPinValueCallbackContext;
        void *currentReportFirmwareCallbackContext;
        void *currentReportVersionCallbackContext;
        void *currentDataBufferOverflowCallbackContext;
        void *currentStringCallbackContext;
        void *currentSysexCallbackContext;
        void *currentSystemResetCallbackContext;

        /* callback functions */
        callbackFunction currentAnalogCallback;
        callbackFunction currentDigitalCallback;
        callbackFunction currentReportAnalogCallback;
        callbackFunction currentReportDigitalCallback;
        callbackFunction currentPinModeCallback;
        callbackFunction currentPinValueCallback;
        dataBufferOverflowCallbackFunction currentDataBufferOverflowCallback;
        stringCallbackFunction currentStringCallback;
        sysexCallbackFunction currentSysexCallback;
        versionCallbackFunction currentReportFirmwareCallback;
        systemCallbackFunction currentReportVersionCallback;
        systemCallbackFunction currentSystemResetCallback;

        /* private methods ------------------------------ */
        bool bufferDataAtPosition(nStream *, const uint8_t data, const size_t pos);

        size_t decodeByteStream(size_t bytec, uint8_t *bytev);

        void processSysexMessage(nStream *);

        void systemReset(nStream *);

        //Forwards the message
        char forward;

        void forwardMessage(char forward, int i);
    };

} // firmata

#endif /* FirmataParser_h */
